#ifndef _RSGVARIABLEVALUE_HPP
#define _RSGVARIABLEVALUE_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Random Shader Generator
 * ----------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Variable Value class.
 *//*--------------------------------------------------------------------*/

#include "rsgDefs.hpp"
#include "rsgVariableType.hpp"
#include "rsgVariable.hpp"
#include "tcuVector.hpp"

#include <algorithm>

namespace rsg
{

union Scalar
{
	int		intVal;
	float	floatVal;
	bool	boolVal;

	Scalar (void)		: intVal	(0) {}
	Scalar (float v)	: floatVal	(v) {}
	Scalar (int v)		: intVal	(v) {}
	Scalar (bool v)		: boolVal	(v) {}

	// Bit-exact compare
	bool operator== (Scalar other) const { return intVal == other.intVal; }
	bool operator!= (Scalar other) const { return intVal != other.intVal; }

	template <typename T> static Scalar min	(void);
	template <typename T> static Scalar max	(void);

	template <typename T> T		as (void) const;
	template <typename T> T&	as (void);
};
DE_STATIC_ASSERT(sizeof(Scalar) == sizeof(deUint32));

template <> inline Scalar Scalar::min<float>	(void)	{ return Scalar((int)0xff800000);	}
template <> inline Scalar Scalar::max<float>	(void)	{ return Scalar((int)0x7f800000);	}
template <> inline Scalar Scalar::min<int>		(void)	{ return Scalar((int)0x80000000);	}
template <> inline Scalar Scalar::max<int>		(void)	{ return Scalar((int)0x7fffffff);	}
template <> inline Scalar Scalar::min<bool>		(void)	{ return Scalar(false);				}
template <> inline Scalar Scalar::max<bool>		(void)	{ return Scalar(true);				}

template <> inline float	Scalar::as<float>	(void) const	{ return floatVal;	}
template <> inline float&	Scalar::as<float>	(void)			{ return floatVal;	}
template <> inline int		Scalar::as<int>		(void) const	{ return intVal;	}
template <> inline int&		Scalar::as<int>		(void)			{ return intVal;	}
template <> inline bool		Scalar::as<bool>	(void) const	{ return boolVal;	}
template <> inline bool&	Scalar::as<bool>	(void)			{ return boolVal;	}

template <int Stride>
class StridedValueRead
{
public:
								StridedValueRead		(const VariableType& type, const Scalar* value) : m_type(type), m_value(value) {}

	const VariableType&			getType					(void) const			{ return m_type;	}
	const Scalar*				getValuePtr				(void) const			{ return m_value;	}

private:
	const VariableType&			m_type;
	const Scalar*				m_value;
};

template <int Stride>
class ConstStridedValueAccess
{
public:
								ConstStridedValueAccess	(void) : m_type(DE_NULL), m_value(DE_NULL) {}
								ConstStridedValueAccess	(const VariableType& type, const Scalar* valuePtr) : m_type(&type), m_value(const_cast<Scalar*>(valuePtr)) {}

	const VariableType&			getType					(void) const			{ return *m_type;			}

	// Read-only access
	ConstStridedValueAccess		component				(int compNdx) const		{ return ConstStridedValueAccess(getType().getElementType(), m_value + Stride*compNdx);													}
	ConstStridedValueAccess		arrayElement			(int elementNdx) const	{ return ConstStridedValueAccess(getType().getElementType(), m_value + Stride*getType().getElementScalarOffset(elementNdx));				}
	ConstStridedValueAccess		member					(int memberNdx) const	{ return ConstStridedValueAccess(getType().getMembers()[memberNdx].getType(), m_value + Stride*getType().getMemberScalarOffset(memberNdx));	}

	float						asFloat					(void) const			{ DE_STATIC_ASSERT(Stride == 1); return m_value->floatVal;	}
	int							asInt					(void) const			{ DE_STATIC_ASSERT(Stride == 1); return m_value->intVal;	}
	bool						asBool					(void) const			{ DE_STATIC_ASSERT(Stride == 1); return m_value->boolVal;	}
	Scalar						asScalar				(void) const			{ DE_STATIC_ASSERT(Stride == 1); return *m_value;			}

	float						asFloat					(int ndx) const			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return m_value[ndx].floatVal;	}
	int							asInt					(int ndx) const			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return m_value[ndx].intVal;	}
	bool						asBool					(int ndx) const			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return m_value[ndx].boolVal;	}
	Scalar						asScalar				(int ndx) const			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return m_value[ndx];			}

	template <typename T>
	T							as						(int ndx) const			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return this->m_value[ndx].template as<T>();	}

	// For assignment: b = a.value()
	StridedValueRead<Stride>	value					(void) const			{ return StridedValueRead<Stride>(getType(), m_value);		}

protected:
	const VariableType*			m_type;
	Scalar*						m_value;	// \note Non-const internal pointer is used so that ValueAccess can extend this class with RW access
};

template <int Stride>
class StridedValueAccess : public ConstStridedValueAccess<Stride>
{
public:
								StridedValueAccess	(void) {}
								StridedValueAccess	(const VariableType& type, Scalar* valuePtr) : ConstStridedValueAccess<Stride>(type, valuePtr) {}

	// Read-write access
	StridedValueAccess			component			(int compNdx)		{ return StridedValueAccess(this->getType().getElementType(), this->m_value + Stride*compNdx);													}
	StridedValueAccess			arrayElement		(int elementNdx)	{ return StridedValueAccess(this->getType().getElementType(), this->m_value + Stride*this->getType().getElementScalarOffset(elementNdx));					}
	StridedValueAccess			member				(int memberNdx)		{ return StridedValueAccess(this->getType().getMembers()[memberNdx].getType(), this->m_value + Stride*this->getType().getMemberScalarOffset(memberNdx));	}

	float&						asFloat				(void)				{ DE_STATIC_ASSERT(Stride == 1); return this->m_value->floatVal;	}
	int&						asInt				(void)				{ DE_STATIC_ASSERT(Stride == 1); return this->m_value->intVal;		}
	bool&						asBool				(void)				{ DE_STATIC_ASSERT(Stride == 1); return this->m_value->boolVal;		}
	Scalar&						asScalar			(void)				{ DE_STATIC_ASSERT(Stride == 1); return *this->m_value;				}

	float&						asFloat				(int ndx)			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return this->m_value[ndx].floatVal;	}
	int&						asInt				(int ndx)			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return this->m_value[ndx].intVal;		}
	bool&						asBool				(int ndx)			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return this->m_value[ndx].boolVal;		}
	Scalar&						asScalar			(int ndx)			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return this->m_value[ndx];				}

	template <typename T>
	T&							as					(int ndx)			{ DE_ASSERT(de::inBounds(ndx, 0, Stride)); return this->m_value[ndx].template as<T>();		}

	template <int SrcStride>
	StridedValueAccess&			operator=			(const StridedValueRead<SrcStride>& value);

	// Helpers, work only in Stride == 1 case
	template <int Size>
	StridedValueAccess&			operator=			(const tcu::Vector<float, Size>& vec);
	StridedValueAccess&			operator=			(float floatVal)	{ asFloat()		= floatVal;	return *this;	}
	StridedValueAccess&			operator=			(int intVal)		{ asInt()		= intVal;	return *this;	}
	StridedValueAccess&			operator=			(bool boolVal)		{ asBool()		= boolVal;	return *this;	}
	StridedValueAccess&			operator=			(Scalar val)		{ asScalar()	= val;		return *this;	}
};

template <int Stride>
template <int SrcStride>
StridedValueAccess<Stride>& StridedValueAccess<Stride>::operator= (const StridedValueRead<SrcStride>& valueRead)
{
	DE_STATIC_ASSERT(SrcStride == Stride || SrcStride == 1);
	DE_ASSERT(this->getType() == valueRead.getType());

	int scalarSize = this->getType().getScalarSize();

	if (scalarSize == 0)
		return *this; // Happens when void value range is copied

	if (Stride == SrcStride)
		std::copy(valueRead.getValuePtr(), valueRead.getValuePtr() + scalarSize*Stride, this->m_value);
	else
	{
		for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
			std::fill(this->m_value + scalarNdx*Stride, this->m_value + (scalarNdx+1)*Stride, valueRead.getValuePtr()[scalarNdx]);
	}

	return *this;
}

template <int Stride>
template <int Size>
StridedValueAccess<Stride>& StridedValueAccess<Stride>::operator= (const tcu::Vector<float, Size>& vec)
{
	DE_ASSERT(this->getType() == VariableType(VariableType::TYPE_FLOAT, Size));
	for (int comp = 0; comp < 4; comp++)
		component(comp).asFloat() = vec.getPtr()[comp];

	return *this;
}

// Typedefs for stride == 1 case
typedef ConstStridedValueAccess<1>	ConstValueAccess;
typedef StridedValueAccess<1>		ValueAccess;

class ConstValueRangeAccess
{
public:
								ConstValueRangeAccess	(void) : m_type(DE_NULL), m_min(DE_NULL), m_max(DE_NULL) {}
								ConstValueRangeAccess	(const VariableType& type, const Scalar* minVal, const Scalar* maxVal) : m_type(&type), m_min(const_cast<Scalar*>(minVal)), m_max(const_cast<Scalar*>(maxVal)) {}

	const VariableType&			getType					(void) const	{ return *m_type;							}
	ConstValueAccess			getMin					(void) const	{ return ConstValueAccess(*m_type, m_min);	}
	ConstValueAccess			getMax					(void) const	{ return ConstValueAccess(*m_type, m_max);	}

	// Read-only access
	ConstValueRangeAccess		component				(int compNdx) const;
	ConstValueRangeAccess		arrayElement			(int elementNdx) const;
	ConstValueRangeAccess		member					(int memberNdx) const;

	// Set operations - tests condition for all elements
	bool						intersects				(const ConstValueRangeAccess& other) const;
	bool						isSupersetOf			(const ConstValueRangeAccess& other) const;
	bool						isSubsetOf				(const ConstValueRangeAccess& other) const;

protected:
	const VariableType*			m_type;
	Scalar*						m_min;	// \note See note in ConstValueAccess
	Scalar*						m_max;
};

inline ConstValueRangeAccess ConstValueRangeAccess::component (int compNdx) const
{
	return ConstValueRangeAccess(m_type->getElementType(), m_min + compNdx, m_max + compNdx);
}

inline ConstValueRangeAccess ConstValueRangeAccess::arrayElement (int elementNdx) const
{
	int offset = m_type->getElementScalarOffset(elementNdx);
	return ConstValueRangeAccess(m_type->getElementType(), m_min + offset, m_max + offset);
}

inline ConstValueRangeAccess ConstValueRangeAccess::member (int memberNdx) const
{
	int offset = m_type->getMemberScalarOffset(memberNdx);
	return ConstValueRangeAccess(m_type->getMembers()[memberNdx].getType(), m_min + offset, m_max + offset);
}

class ValueRangeAccess : public ConstValueRangeAccess
{
public:
								ValueRangeAccess		(const VariableType& type, Scalar* minVal, Scalar* maxVal) : ConstValueRangeAccess(type, minVal, maxVal) {}

	// Read-write access
	ValueAccess					getMin					(void) { return ValueAccess(*m_type, m_min);	}
	ValueAccess					getMax					(void) { return ValueAccess(*m_type, m_max);	}

	ValueRangeAccess			component				(int compNdx);
	ValueRangeAccess			arrayElement			(int elementNdx);
	ValueRangeAccess			member					(int memberNdx);
};

inline ValueRangeAccess ValueRangeAccess::component (int compNdx)
{
	return ValueRangeAccess(m_type->getElementType(), m_min + compNdx, m_max + compNdx);
}

inline ValueRangeAccess ValueRangeAccess::arrayElement (int elementNdx)
{
	int offset = m_type->getElementScalarOffset(elementNdx);
	return ValueRangeAccess(m_type->getElementType(), m_min + offset, m_max + offset);
}

inline ValueRangeAccess ValueRangeAccess::member (int memberNdx)
{
	int offset = m_type->getMemberScalarOffset(memberNdx);
	return ValueRangeAccess(m_type->getMembers()[memberNdx].getType(), m_min + offset, m_max + offset);
}

class ValueRange
{
public:
								ValueRange			(const VariableType& type);
								ValueRange			(const VariableType& type, const ConstValueAccess& minVal, const ConstValueAccess& maxVal);
								ValueRange			(const VariableType& type, const Scalar* minVal, const Scalar* maxVal);
								ValueRange			(ConstValueRangeAccess other);
								~ValueRange			(void);

	const VariableType&			getType				(void) const	{ return m_type;								}

	ValueAccess					getMin				(void)			{ return ValueAccess(m_type, getMinPtr());		}
	ValueAccess					getMax				(void)			{ return ValueAccess(m_type, getMaxPtr());		}

	ConstValueAccess			getMin				(void) const	{ return ConstValueAccess(m_type, getMinPtr());	}
	ConstValueAccess			getMax				(void) const	{ return ConstValueAccess(m_type, getMaxPtr());	}

	ValueRangeAccess			asAccess			(void)			{ return ValueRangeAccess(m_type, getMinPtr(), getMaxPtr());		}
	ConstValueRangeAccess		asAccess			(void) const	{ return ConstValueRangeAccess(m_type, getMinPtr(), getMaxPtr());	}

	operator ConstValueRangeAccess					(void) const	{ return asAccess();							}
	operator ValueRangeAccess						(void)			{ return asAccess();							}

	static void					computeIntersection	(ValueRangeAccess dst, const ConstValueRangeAccess& a, const ConstValueRangeAccess& b);
	static void					computeIntersection	(ValueRange& dst, const ConstValueRangeAccess& a, const ConstValueRangeAccess& b);

private:
	const Scalar*				getMinPtr			(void) const	{ return m_min.empty() ? DE_NULL : &m_min[0];	}
	const Scalar*				getMaxPtr			(void) const	{ return m_max.empty() ? DE_NULL : &m_max[0];	}

	Scalar*						getMinPtr			(void)			{ return m_min.empty() ? DE_NULL : &m_min[0];	}
	Scalar*						getMaxPtr			(void)			{ return m_max.empty() ? DE_NULL : &m_max[0];	}

	VariableType				m_type;
	std::vector<Scalar>			m_min;
	std::vector<Scalar>			m_max;
};

template <int Stride>
class ValueStorage
{
public:
										ValueStorage		(void);
										ValueStorage		(const VariableType& type);

	void								setStorage			(const VariableType& type);

	StridedValueAccess<Stride>			getValue			(const VariableType& type)			{ return StridedValueAccess<Stride>(type, &m_value[0]);			}
	ConstStridedValueAccess<Stride>		getValue			(const VariableType& type) const	{ return ConstStridedValueAccess<Stride>(type, &m_value[0]);	}

private:
										ValueStorage		(const ValueStorage& other);
	ValueStorage						operator=			(const ValueStorage& other);

	std::vector<Scalar>					m_value;
};

template <int Stride>
ValueStorage<Stride>::ValueStorage (void)
{
}

template <int Stride>
ValueStorage<Stride>::ValueStorage (const VariableType& type)
{
	setStorage(type);
}

template <int Stride>
void ValueStorage<Stride>::setStorage (const VariableType& type)
{
	m_value.resize(type.getScalarSize() * Stride);
}

class VariableValue
{
public:
							VariableValue		(const Variable* variable) : m_variable(variable), m_storage(m_variable->getType()) {}
							~VariableValue		(void) {}

	const Variable*			getVariable			(void) const	{ return m_variable;								}
	ValueAccess				getValue			(void)			{ return m_storage.getValue(m_variable->getType());	}
	ConstValueAccess		getValue			(void) const	{ return m_storage.getValue(m_variable->getType());	}

							VariableValue		(const VariableValue& other);
	VariableValue&			operator=			(const VariableValue& other);

private:
	const VariableType&		getType				(void) const	{ return m_variable->getType();						}

	const Variable*			m_variable;
	ValueStorage<1>			m_storage;
};

} // rsg

#endif // _RSGVARIABLEVALUE_HPP
